﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Configuration;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;

using LongNet.Config;

namespace LongNet.Util
{
    public class HttpDownLoad
    {
        private static LogManager.Logger logger = LogManager.GetCurrentClassLogger();
        public event DownComplatedEventHandler DownComplated;

        private const string ImageRegex = @"http://[^<>{},;]+\.(jpg|png|gif)";
        private const string UrlRegex = @"http://(www\.)?[^<>{},;(www)]+\.(html|htm|php|asp|aspx)(\?[^<>,;\s" + "\"" + "]*)";
        private const string HrefReg = @"(h|H)(r|R)(e|E)(f|F) *= *('|"")?(http://|https://)((\w|\\|\/|\.|:|-|_)+)('|""| *|>)?";

        private static readonly object syncBoot = new object();
        private static readonly object syncThread = new object();
        private static readonly object syncDeep = new object();
        private static readonly object syncSettings = new object();
        private const int MaxThread = 50;
        private int ThreadCount = 0;       
        private int MaxDeep = 5;
        private string domain = "";
        private string downLoadSettingsPath = "";
        private DownLoadSettings downLoadSettings = new DownLoadSettings();
        #region Properties
        #endregion

        #region Constructor

        public HttpDownLoad(string _domain)
        {
            MaxDeep = Utils.StringToInt(HttpUtil.GetAppSettings("LongNet.MaxDeep", false, false), 1);
            domain = _domain;
            downLoadSettingsPath = ConfigManager.LongNetConfigFolder + "\\" + _domain + ".config";
            if (!File.Exists(downLoadSettingsPath))
            {
                //File.Create(downLoadSettingsPath);
                Serializer.ConvertToXmlFile(downLoadSettings, downLoadSettingsPath);
            }
            else
            {
                lock (syncSettings)
                {
                    downLoadSettings = Serializer.ConvertFromXmlFile<DownLoadSettings>(downLoadSettingsPath);
                }
            }
        }
        ~ HttpDownLoad()
        {
            lock (syncSettings)
            {
                Serializer.ConvertToXmlFile(downLoadSettings, downLoadSettingsPath);
            }
        }
        #endregion

        
        #region Methods    

        #region  线程深度下载链接图片
        public void DeepDownLinkImage(string url)
        {
            Thread th = new Thread(new ParameterizedThreadStart(StartDownThread));
            th.IsBackground = true;
            th.Start(new DownThreadArge(1, url));
        }

        protected void StartDownThread(object obj)
        {
            DownThreadArge arge = (DownThreadArge)obj;
            Down(arge.Deep, arge.Link);
        }
        /// <summary>
        /// 根据深度下载指定的链接页面图片
        /// </summary>
        /// <param name="deep"></param>
        /// <param name="url"></param>
        protected void Down(int deep, string url)
        {
            //线程数目增加
            lock (syncThread)
            {
                if (ThreadCount <= MaxThread)
                {
                    ThreadCount ++;
                }
            }
            var tempFileName = "";
            //如果线程数目最大，则等待
            bool candown = false;
            while (true)
            {
                lock (syncThread)
                {
                    if (ThreadCount <= MaxThread)
                    {
                        candown = true;
                    }
                }
                if (candown)
                {
                    //根据连接查询配置
                   var link= downLoadSettings.DownLoadInfos.SingleOrDefault((d)=>d.LinkName.Equals(url));
                   string html = HttpUtil.HttpGet(url, domain, link == null, out tempFileName);
                   //没有link的配置，则保存
                    if (link == null)
                    {
                        link = new DownLoadInfo(url, tempFileName, DateTime.Now.ToString());
                        downLoadSettings.DownLoadInfos.Add(link);
                        lock (syncSettings)
                        {
                            Serializer.ConvertToXmlFile(downLoadSettings, downLoadSettingsPath);
                        }
                    }
                    //下载图片
                    List<string> images = GetMatchList(ImageRegex, html).ToList();
                    images.ForEach(
                    img =>
                    {
                        //HttpUtil.HttpGetImage(Regex.Replace(img, @"\s*((h|H)(r|R)(e|E)(f|F)\s*=)|""\s*", ""), null);
                        //根据连接查询图片配置
                        var templink = Regex.Replace(img, @"\s*((h|H)(r|R)(e|E)(f|F)\s*=)|""\s*", "");
                        var imagelink = downLoadSettings.DownLoadInfos.SingleOrDefault((d) => d.LinkName.Equals(templink));
                        if(imagelink==null)
                        {
                            HttpUtil.HttpGetImage(templink, domain, true, out tempFileName);
                            imagelink = new DownLoadInfo(templink, tempFileName, DateTime.Now.ToString());
                            downLoadSettings.DownLoadInfos.Add(imagelink);
                            lock (syncSettings)
                            {
                                Serializer.ConvertToXmlFile(downLoadSettings, downLoadSettingsPath);
                            }
                        }

                    });

                    //下载CCS
                    //下载一切资源
                    var request = (HttpWebRequest)HttpWebRequest.Create(url);
                    string httpRegex = @"(h|H)(t|T)(t|T)(p|P)://[^<>{},;]+\.(css|js)";
                    List<string> resource = GetMatchList(httpRegex, html).ToList();
                    resource.ForEach(
                    r =>
                    {                       
                        //根据连接查询资源配置                     
                        var resourcelink = downLoadSettings.DownLoadInfos.SingleOrDefault((d) => d.LinkName.Equals(r));
                        if (resourcelink == null)
                        {
                            HttpUtil.HttpGet(r, domain, true, out tempFileName);
                            resourcelink = new DownLoadInfo(r, tempFileName, DateTime.Now.ToString());
                            downLoadSettings.DownLoadInfos.Add(resourcelink);
                            lock (syncSettings)
                            {
                                Serializer.ConvertToXmlFile(downLoadSettings, downLoadSettingsPath);
                            }
                        }

                    });
                    //如果深度大于最大，退出下载
                    if (deep == MaxDeep)
                    {
                        lock (syncThread)
                        {
                            ThreadCount--;
                        }
                        this.OnDownComplated(null, new DownComplatedEventArgs(deep, url));                      
                    }
                    else
                    {
                        deep += 1;
                        //下载网页
                        List<string> list = GetMatchList(HrefReg, html).ToList();
                        list.ForEach(
                        s =>
                        {
                            //新线程去下载深度链接
                            Thread th = new Thread(new ParameterizedThreadStart(StartDownThread));
                            th.IsBackground = true;
                            th.Start(new DownThreadArge(deep, Regex.Replace(s, @"\s*((h|H)(r|R)(e|E)(f|F)\s*=)|""\s*", "")));
                            //Down(deep, s);             

                        });
                    }
                    break;
                }
            }           
            lock (syncThread)
            {
                if (ThreadCount==1)
                {
                    //完成事件
                    this.OnDownComplated(null, null);
                }
            }
        }
        public IList<string> GetMatchList(string regex, string input)
        {
            IList<string> list = new List<string>();
            MatchCollection matchs = Regex.Matches(input, regex, RegexOptions.Singleline);
            for (int i = 0; i < matchs.Count; i++)
            {
                list.Add(matchs[i].Value);
            }
            return list;
        }
        private void OnDownComplated(object sender, DownComplatedEventArgs e)
        {
            if (this.DownComplated != null)
                this.DownComplated(sender, e);

            if (logger.IsDebugEnabled)
                logger.Debug(string.Format("OnDownComplated:{0},{1}",e.Link,e.Deep));
        }

        #endregion 线程深度下载链接图片

        #endregion



        public delegate void DownComplatedEventHandler(object sender, DownComplatedEventArgs e);

    }
    /// <summary>
    /// 下载完成参数
    /// </summary>
    public class DownComplatedEventArgs : EventArgs
    {
        public DownComplatedEventArgs(int deep, string link)
        {
            Deep = deep;
            Link = link;

        }
        public int Deep { set; get; }
        public string Link { set; get; }        
    }

    [Serializable]
    public class DownThreadArge 
    {
        public DownThreadArge(int deep, string link)
        {
            Deep = deep;
            Link = link;

        }
        public int Deep { set; get; }
        public string Link { set; get; }

    }

    [Serializable]
    public class DownLoadInfo
    {
        public string LinkName { set; get; }
        public string FileName { set; get; }
        public string Date { set; get; }

        public DownLoadInfo()
        {
            LinkName = string.Empty;
            FileName = string.Empty;
            Date = string.Empty;
        }
        public DownLoadInfo(string linkName, string fileName, string date)
        {
            LinkName = linkName;
            FileName = fileName;
            Date = date;
        }
    }

    [Serializable]
    public class DownLoadSettings
    {
        public List<DownLoadInfo> DownLoadInfos { set; get; }

        public DownLoadSettings()
        {
            DownLoadInfos = new List<DownLoadInfo>();
        }
      
    }
}
